XUCERT1 ;ISD/HGW Kernel PKI Certificate Utilities (cont) ;09/17/2019 15:25
;;8.0;KERNEL;**659,701**;Jul 10, 1995;Build 11
;Per VA Directive 6402, this routine should not be modified.
;
Q
VAL1(DOC,SIG) ;Function. Validate Document (Cache 2015.2 or greater)
;ZEXCEPT: Document,ValidateDocument ;Object Script
N XUDOC,XUSTATUS
S XUDOC=DOC.Document ;Create the OREF
I $G(XUDOC)="" Q "-1^Failed to import XML document"
D XUDOC.AddIDs() ; p701
S XUSTATUS=SIG.ValidateDocument(XUDOC)
I $G(XUSTATUS)["Failed" Q "-1^Failed data integrity or signature validation check"
Q 1
;
VAL2(DOC,SIG,ERR) ;Function. Validate Document (Less than Cache 2015.2)
N ERROR,STATUS
S STATUS=1
S ERROR=""
I '$$CHKDATA(DOC,SIG) S ERR("DIGEST")="" S STATUS=0 ; check integrity
I '$$CHKSIGN(DOC,SIG,.ERR) S STATUS=0 ; check signature is valid
Q STATUS=1
;
READER(DOC) ;Function. Reads XML Document
;ZEXCEPT: %New,%XML,OpenFile,OpenStream,Reader,class ;Object Script
N XUIN,XUREAD,XUSC
S XUREAD=##class(%XML.Reader).%New() ;Create OREF instance in memory
I $E(DOC)="^" D
. S XUIN=$$LOADSTRM(DOC) ;Extract stream from global
. S XUSC=XUREAD.OpenStream(XUIN) ;Import from stream
E D
. S XUSC=XUREAD.OpenFile(DOC) ;Import from file
I $G(XUSC)'=1 Q "-1^"_$G(XUSC)
Q XUREAD
;
SGNTR(READER) ;Function. Finds digital signature
N SIGNATURE,STATUS
D READER.Correlate("Signature","%XML.Security.Signature")
D READER.Next(.SIGNATURE,.STATUS)
I $G(SIGNATURE)="" Q "-1^NO-SIGNATURE"
Q SIGNATURE
;
CHKDATA(READER,SIG) ;Function. Check integrity of signed data
; by comparing computed digest with incoming digest value
N COMPUTED
S COMPUTED=$$DIGESTCP(READER,SIG)
Q COMPUTED=$$DIGEST(SIG)
;
DIGESTCP(READER,SIG) ;Function. Compute SHA digest value
;ZEXCEPT: %New,%XML,ComputeSha1Digest,Document,GetNode,NodeId,Writer,class
N NODE,WRITER,BITLENGT,ISSTR,MIME,SIGNNODE,PREFIXL,CANONTXT
S NODE=READER.Document.GetNode("")
S NODE.NodeId=$$REFNODE(READER)
S SIGNNODE=SIG.NodeId
S WRITER=##class(%XML.Writer).%New()
; p701
S PREFIXL="" ; explicit, xml-exc-c14n#
;S BITLENGT=160
S BITLENGT=256 ;
; end p701
S ISSTR=0
S MIME=""
Q SIG.ComputeSha1Digest(NODE,SIGNNODE,WRITER,.PREFIXL,BITLENGT,ISSTR,.CANONTXT,MIME)
;
REFNODE(READER) ;Function. Get reference node which is Assertion node since GetNodeById can't find "ID"
;ZEXCEPT: NodeId,STATUS
N ASSERTION
D READER.Rewind()
D READER.Correlate("Assertion","%SAML.Assertion")
D READER.Next(.ASSERTION,.STATUS)
Q ASSERTION.NodeId
;
DIGEST(SIGNATURE) ;Function. Find incoming digest value
;ZEXCEPT: DigestValue,GetAt,Reference,SignedInfo
N REF
S REF=SIGNATURE.SignedInfo.Reference.GetAt(1)
Q REF.DigestValue
;
CHKSIGN(READER,SIGNATURE,ERR) ;Function. Validate digital signature
; Return value: 1 if the signature was successfully verified, 0 otherwise.
;ZEXCEPT: %New,%XML,Canonicalize,Certificate,Document,Encryption,GetNode,GetXMLString,KeyInfo,NodeId,OutputToString,RSASHAVerify,SignatureValue,SignedInfo,ValidateTokenRef,Writer,X509Credentials,class
N BITLENGT,CAFILE,CERT,CRLFILE,ERROR,SIGNTXT,SIGNVAL,STATUS
S ERROR=""
S BITLENGT=256 ; (Integer) Length in bits of desired hash, where 256 is SHA-256
S SIGNTXT=$$SIGNTEXT(READER,SIGNATURE) ; (String) Data that was signed
S SIGNVAL=SIGNATURE.SignatureValue ; (String) Signature to be verified
S CERT=$$CERT(SIGNATURE) ; (String) X.509 certificate containing the RSA public key to validate the signature
;P701
I +CERT=-1 S ERR("CERT")=""
S CAFILE=$System.Util.ManagerDirectory()_"cache.cer"
I '##class(%File).Exists(CAFILE) S ERR("CAFILE")=""
;S CRLFILE=$zu(12)_"cache.crl"
;I $zu(140,4,CRLFILE)'=0 Set CRLFILE=""
;RSASHAVerify works with OpenSSL on Windows and Linux, but crashes with VMS.
I $$VERSION^%ZOSV(1)["OpenVMS" Q 1 ;Quit if VMS, skip signature validation
I $D(ERR("CAFILE")) D
. S STATUS=$System.Encryption.RSASHAVerify(BITLENGT,SIGNTXT,SIGNVAL,CERT)
E S STATUS=$System.Encryption.RSASHAVerify(BITLENGT,SIGNTXT,SIGNVAL,CERT,CAFILE)
I 'STATUS S ERR("SIGNATURE")="" Q 0
Q 1
;
SIGNTEXT(READER,SIGNATURE) ;Function. Retrieves the SignedInfo text
;ZEXCEPT: %New,%XML,Canonicalize,Document,GetNode,GetXMLString,NodeId,OutputToString,SignedInfo,Writer,class ;ObjectScript
N NODE,PREFARR,WRITER,SC
S NODE=READER.Document.GetNode("")
S NODE.NodeId=SIGNATURE.SignedInfo.NodeId
; p701 explicit canonicalization, xml-exc-c14n#, make PREFARR undefined
;S PREFARR="c14n" ; signing prefix array
S WRITER=##class(%XML.Writer).%New()
S SC=WRITER.OutputToString()
S SC=WRITER.Canonicalize(NODE,.PREFARR)
Q WRITER.GetXMLString(.SC) ; SignedInfo
;
CERT(SIG) ;Function. Retrieves a certificate
;ZEXCEPT: Certificate,KeyInfo,ValidateTokenRef,X509Credentials ;ObjectScript
N KEYINFO,ERROR
S KEYINFO=SIG.KeyInfo
S ERROR=KEYINFO.ValidateTokenRef("")
I ERROR'="" Q "-1^Invalid KeyInfo"
Q KEYINFO.X509Credentials.Certificate
;
LOADSTRM(GLO) ;Intrinsic Function. Load global into stream
;ZEXCEPT: %New,%Stream,GlobalCharacter,class ;ObjectScript
N GLOREF,I,X,XMLSTRM,XQ,Y
S Y=GLO
S XQ=$P(Y,")") ;or use $$OREF^DILF(closed_root) to convert closed root to open root?
S XMLSTRM=##class(%Stream.TmpCharacter).%New() ;Create OREF instance in memory. p701 instead of GlobalCharacter
;Read XML from global, starting at the beginning, into XMLSTRM
F I=0:0 D Q:Y'[XQ
. S Y=$Q(@Y) Q:Y'[XQ
. S X=$G(@Y)
. D XMLSTRM.Write(X)
Q XMLSTRM
;
ADDERR(RES,ERR) ;
S RES(ERR)=""
Q
;
--- Routine Detail --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HXUCERT1 5551 printed Dec 13, 2024@02:09:02 Page 2
XUCERT1 ;ISD/HGW Kernel PKI Certificate Utilities (cont) ;09/17/2019 15:25
+1 ;;8.0;KERNEL;**659,701**;Jul 10, 1995;Build 11
+2 ;Per VA Directive 6402, this routine should not be modified.
+3 ;
+4 QUIT
VAL1(DOC,SIG) ;Function. Validate Document (Cache 2015.2 or greater)
+1 ;ZEXCEPT: Document,ValidateDocument ;Object Script
+2 NEW XUDOC,XUSTATUS
+3 ;Create the OREF
SET XUDOC=DOC.Document
+4 IF $GET(XUDOC)=""
QUIT "-1^Failed to import XML document"
+5 ; p701
DO XUDOC.AddIDs()
+6 SET XUSTATUS=SIG.ValidateDocument(XUDOC)
+7 IF $GET(XUSTATUS)["Failed"
QUIT "-1^Failed data integrity or signature validation check"
+8 QUIT 1
+9 ;
VAL2(DOC,SIG,ERR) ;Function. Validate Document (Less than Cache 2015.2)
+1 NEW ERROR,STATUS
+2 SET STATUS=1
+3 SET ERROR=""
+4 ; check integrity
IF '$$CHKDATA(DOC,SIG)
SET ERR("DIGEST")=""
SET STATUS=0
+5 ; check signature is valid
IF '$$CHKSIGN(DOC,SIG,.ERR)
SET STATUS=0
+6 QUIT STATUS=1
+7 ;
READER(DOC) ;Function. Reads XML Document
+1 ;ZEXCEPT: %New,%XML,OpenFile,OpenStream,Reader,class ;Object Script
+2 NEW XUIN,XUREAD,XUSC
+3 ;Create OREF instance in memory
SET XUREAD=##class(%XML.Reader).%New()
+4 IF $EXTRACT(DOC)="^"
Begin DoDot:1
+5 ;Extract stream from global
SET XUIN=$$LOADSTRM(DOC)
+6 ;Import from stream
SET XUSC=XUREAD.OpenStream(XUIN)
End DoDot:1
+7 IF '$TEST
Begin DoDot:1
+8 ;Import from file
SET XUSC=XUREAD.OpenFile(DOC)
End DoDot:1
+9 IF $GET(XUSC)'=1
QUIT "-1^"_$GET(XUSC)
+10 QUIT XUREAD
+11 ;
SGNTR(READER) ;Function. Finds digital signature
+1 NEW SIGNATURE,STATUS
+2 DO READER.Correlate("Signature","%XML.Security.Signature")
+3 DO READER.Next(.SIGNATURE,.STATUS)
+4 IF $GET(SIGNATURE)=""
QUIT "-1^NO-SIGNATURE"
+5 QUIT SIGNATURE
+6 ;
CHKDATA(READER,SIG) ;Function. Check integrity of signed data
+1 ; by comparing computed digest with incoming digest value
+2 NEW COMPUTED
+3 SET COMPUTED=$$DIGESTCP(READER,SIG)
+4 QUIT COMPUTED=$$DIGEST(SIG)
+5 ;
DIGESTCP(READER,SIG) ;Function. Compute SHA digest value
+1 ;ZEXCEPT: %New,%XML,ComputeSha1Digest,Document,GetNode,NodeId,Writer,class
+2 NEW NODE,WRITER,BITLENGT,ISSTR,MIME,SIGNNODE,PREFIXL,CANONTXT
+3 SET NODE=READER.Document.GetNode("")
+4 SET NODE.NodeId=$$REFNODE(READER)
+5 SET SIGNNODE=SIG.NodeId
+6 SET WRITER=##class(%XML.Writer).%New()
+7 ; p701
+8 ; explicit, xml-exc-c14n#
SET PREFIXL=""
+9 ;S BITLENGT=160
+10 ;
SET BITLENGT=256
+11 ; end p701
+12 SET ISSTR=0
+13 SET MIME=""
+14 QUIT SIG.ComputeSha1Digest(NODE,SIGNNODE,WRITER,.PREFIXL,BITLENGT,ISSTR,.CANONTXT,MIME)
+15 ;
REFNODE(READER) ;Function. Get reference node which is Assertion node since GetNodeById can't find "ID"
+1 ;ZEXCEPT: NodeId,STATUS
+2 NEW ASSERTION
+3 DO READER.Rewind()
+4 DO READER.Correlate("Assertion","%SAML.Assertion")
+5 DO READER.Next(.ASSERTION,.STATUS)
+6 QUIT ASSERTION.NodeId
+7 ;
DIGEST(SIGNATURE) ;Function. Find incoming digest value
+1 ;ZEXCEPT: DigestValue,GetAt,Reference,SignedInfo
+2 NEW REF
+3 SET REF=SIGNATURE.SignedInfo.Reference.GetAt(1)
+4 QUIT REF.DigestValue
+5 ;
CHKSIGN(READER,SIGNATURE,ERR) ;Function. Validate digital signature
+1 ; Return value: 1 if the signature was successfully verified, 0 otherwise.
+2 ;ZEXCEPT: %New,%XML,Canonicalize,Certificate,Document,Encryption,GetNode,GetXMLString,KeyInfo,NodeId,OutputToString,RSASHAVerify,SignatureValue,SignedInfo,ValidateTokenRef,Writer,X509Credentials,class
+3 NEW BITLENGT,CAFILE,CERT,CRLFILE,ERROR,SIGNTXT,SIGNVAL,STATUS
+4 SET ERROR=""
+5 ; (Integer) Length in bits of desired hash, where 256 is SHA-256
SET BITLENGT=256
+6 ; (String) Data that was signed
SET SIGNTXT=$$SIGNTEXT(READER,SIGNATURE)
+7 ; (String) Signature to be verified
SET SIGNVAL=SIGNATURE.SignatureValue
+8 ; (String) X.509 certificate containing the RSA public key to validate the signature
SET CERT=$$CERT(SIGNATURE)
+9 ;P701
+10 IF +CERT=-1
SET ERR("CERT")=""
+11
*** ERROR ***
SET CAFILE=$System.Util.ManagerDirectory()_"cache.cer"
+12 IF '##class(%File).Exists(CAFILE)
SET ERR("CAFILE")=""
+13 ;S CRLFILE=$zu(12)_"cache.crl"
+14 ;I $zu(140,4,CRLFILE)'=0 Set CRLFILE=""
+15 ;RSASHAVerify works with OpenSSL on Windows and Linux, but crashes with VMS.
+16 ;Quit if VMS, skip signature validation
IF $$VERSION^%ZOSV(1)["OpenVMS"
QUIT 1
+17 IF $DATA(ERR("CAFILE"))
Begin DoDot:1
+18
*** ERROR ***
SET STATUS=$System.Encryption.RSASHAVerify(BITLENGT,SIGNTXT,SIGNVAL,CERT)
End DoDot:1
+19 IF '$TEST