- XUCERT1 ;ISD/HGW Kernel PKI Certificate Utilities (cont) ;09/17/2019 15:25
- ;;8.0;KERNEL;**659,701**;Jul 10, 1995;Build 0
- ;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 5550 printed Feb 18, 2025@23:35:28 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 0
- +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